//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. 
//

.intel_syntax noprefix
#include "unixasmmacros.inc"

// This is the number of times a successful chain lookup will occur before the
// entry is promoted to the front of the chain. This is declared as extern because
// the default value (CALL_STUB_CACHE_INITIAL_SUCCESS_COUNT) is defined in the header.
// extern size_t g_dispatch_cache_chain_success_counter;
CHAIN_SUCCESS_COUNTER = g_dispatch_cache_chain_success_counter

// The reason for not using .equ or '=' here is that otherwise the assembler compiles e.g. 
// mov rax, BACKPATCH_FLAG as mov rax, [BACKPATCH_FLAG]
#define BACKPATCH_FLAG             1        // Also known as SDF_ResolveBackPatch    in the EE
#define PROMOTE_CHAIN_FLAG         2        // Also known as SDF_ResolvePromoteChain in the EE
#define INITIAL_SUCCESS_COUNT      0x100

// On Input:
//    r11                    contains the address of the indirection cell (with the flags in the low bits)
//  [rsp+0] m_Datum:         contains the dispatch token  (slot number or MethodDesc) for the target
//                                 or the ResolveCacheElem when r11 has the PROMOTE_CHAIN_FLAG set
//  [rsp+8] m_ReturnAddress: contains the return address of caller to stub
        
NESTED_ENTRY ResolveWorkerAsmStub, _TEXT

        PROLOG_WITH_TRANSITION_BLOCK 0, 8, rdx

        // token stored in rdx by prolog

        lea             rdi, [rsp + __PWTB_TransitionBlock]        // pTransitionBlock
        mov             rsi, r11                                   // indirection cell + flags
        mov             rcx,  rsi
        and             rcx,  7                                    // flags
        sub             rsi, rcx                                   // indirection cell

        call            VSD_ResolveWorker

        EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
        TAILJMP_RAX

NESTED_END ResolveWorkerAsmStub, _TEXT
        
// extern void ResolveWorkerChainLookupAsmStub()
LEAF_ENTRY ResolveWorkerChainLookupAsmStub, _TEXT
// This will perform a quick chained lookup of the entry if the initial cache lookup fails
// On Input:  
//   rsi       contains our type     (MethodTable)
//   r10       contains our contract (DispatchToken)
//   r11       contains the address of the indirection (and the flags in the low two bits)
// [rsp+0x00]  contains the pointer to the ResolveCacheElem
// [rsp+0x08]  contains the saved value of rsi
// [rsp+0x10]  contains the return address of caller to stub
// 
        mov     rax, BACKPATCH_FLAG  // First we check if r11 has the BACKPATCH_FLAG set
        and     rax, r11             // Set the flags based on (BACKPATCH_FLAG and r11)
        pop     rax                  // pop the pointer to the ResolveCacheElem from the top of stack (leaving the flags unchanged)
        jnz     Fail_RWCLAS          // If the BACKPATCH_FLAGS is set we will go directly to the ResolveWorkerAsmStub
        
MainLoop_RWCLAS:
        mov     rax, [rax+18h]   // get the next entry in the chain (don't bother checking the first entry again)
        test    rax,rax          // test if we hit a terminating NULL
        jz      Fail_RWCLAS

        cmp    rsi, [rax+00h]    // compare our MT with the one in the ResolveCacheElem
        jne    MainLoop_RWCLAS
        cmp    r10, [rax+08h]    // compare our DispatchToken with one in the ResolveCacheElem
        jne    MainLoop_RWCLAS
Success_RWCLAS:        
        PREPARE_EXTERNAL_VAR CHAIN_SUCCESS_COUNTER, rsi
        sub    qword ptr [rsi],1 // decrement success counter 
        jl     Promote_RWCLAS
        mov    rax, [rax+10h]    // get the ImplTarget
        pop    rsi
        jmp    rax
        
Promote_RWCLAS:                  // Move this entry to head postion of the chain
        // be quick to reset the counter so we don't get a bunch of contending threads
        mov    qword ptr [rsi], INITIAL_SUCCESS_COUNT
        or     r11, PROMOTE_CHAIN_FLAG
        mov    r10, rax          // We pass the ResolveCacheElem to ResolveWorkerAsmStub instead of the DispatchToken 
Fail_RWCLAS:           
        pop    rsi               // Restore the original saved rdx value
        push   r10               // pass the DispatchToken or ResolveCacheElem to promote to ResolveWorkerAsmStub
        
        jmp    ResolveWorkerAsmStub
        
LEAF_END ResolveWorkerChainLookupAsmStub, _TEXT
